{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[toc]\n",
    "\n",
    "# 安装\n",
    "\n",
    "```sh\n",
    "pip install --upgrade pyinstaller\n",
    "```\n",
    "\n",
    "安装最新开发版\n",
    "\n",
    "```sh\n",
    "pip install https://github.com/pyinstaller/pyinstaller/tarball/develop\n",
    "```\n",
    "\n",
    "验证安装\n",
    "\n",
    "```sh\n",
    "pyinstaller -v\n",
    "```\n",
    "\n",
    "结果应当输出类似与 3.x 或者 3.n.dev0-xxxxxx 的开发版本号\n",
    "\n",
    "## 💊温馨提示\n",
    "\n",
    "1. **在虚拟环境下打包**，只安装程序需要的依赖包，避免打包后文件过大\n",
    "2. 在打包成单个 exe 文件之前，请先尝试打包成**一个文件夹**测试是否正常运行\n",
    "3. 打包成单个文件启动较慢，原因是程序需要按原目录结构解压到临时目录下，在用户目录下生成临时文件，结束后会自动删除\n",
    "\n",
    "# 用法\n",
    "\n",
    "## 基本用法\n",
    "\n",
    "### 1. 直接打包\n",
    "\n",
    "```sh\n",
    "pyinstaller  myscript.py\n",
    "```\n",
    "\n",
    "### 2. 打包成单文件\n",
    "\n",
    "```sh\n",
    "pyinstaller -F myscript.py\n",
    "```\n",
    "\n",
    "### 3. 去掉控制台窗口，黑窗口\n",
    "\n",
    "不显示控制台窗口(类似 cmd 的黑框框), 如果你写的是带 UI 的程序, 此选项基本必选.如果入口程序是 pyw 文件, 此选项默认生效.\n",
    "\n",
    "```sh\n",
    "pyinstaller -w myscript.py\n",
    "```\n",
    "\n",
    "### 4. 添加图标\n",
    "\n",
    "```sh\n",
    "pyinstaller -i icon.ico myscript.py\n",
    "```\n",
    "\n",
    "## 高级用法\n",
    "\n",
    "打包一遍后，会生成一个`xxx.spec`文件, 是一个打包参数配置文件，该文件内容由上一次打包命令决定，可以通过添加打包参数重新生成，也可以可以直接修改其内容供下次使用。\n",
    "\n",
    "😘 注： spec 文件生成方法\n",
    "\n",
    "    将 `pyinstaller` 命令换成 `pyi-makespec`\n",
    "\n",
    "    例：\n",
    "    ```sh\n",
    "    pyi-makespec -F filename\n",
    "    ```\n",
    "\n",
    "💊 注：当你修改过 spec 文件并且想将其应用，你需要以下命令\n",
    "\n",
    "    ```sh\n",
    "    pyinstaller ***.spec\n",
    "    ```\n",
    "\n",
    "### 1. 隐式导入\n",
    "\n",
    "有些模块在代码里是动态加载的，比如通过 `__import__(xxx)` 或者 `importlib` 模块加载的，在打包过程中是无法检测出来的，但是不打包进去这些模块，程序会抛出 `ModuleNotFoundError: No module named 'xxx'` 异常，这时候你有两种方法解决。一是，在代码里显式地 import 这些模块，尽管你不去使用（pycharm 里显示灰色），二是在打包时添加打包参数，可以通过命令行参数，也可通过改 xxx.spec 配置文件。\n",
    "\n",
    "#### 命令行模式：\n",
    "\n",
    "可以多次使用\n",
    "\n",
    "> --hidden-import MODULENAME1, --hiddenimport MODULENAME2\n",
    "\n",
    "例：\n",
    "\n",
    "```sh\n",
    "--hidden-import docx --hidden-import Pillow\n",
    "```\n",
    "\n",
    "#### 修改 spec 模式：\n",
    "\n",
    "> hiddenimports=['docx'],\n",
    "\n",
    "例：\n",
    "\n",
    "```ini\n",
    "hiddenimports=['docx', 'Pillow']\n",
    "```\n",
    "\n",
    "### 2. 打包静态文件\n",
    "\n",
    "当你程序中需要读取一些静态文件比如 `setting.json` ,就需要你将他们一起打包进去，否则 pyinstaller 也会忽略他们。\n",
    "\n",
    "#### 命令行模式\n",
    "\n",
    "可以多次使用\n",
    "\n",
    "```sh\n",
    "--add-data <SRC;DEST or SRC:DEST>\n",
    "```\n",
    "\n",
    "👉 注意：格式为 一个原文件名和目**标文件夹**名！，中间用一个分号或者冒号分割。\n",
    "\n",
    "👉 注意：路径中需要用 双反斜杠！！\n",
    "\n",
    "例：  \n",
    "将原路径（绝对路径或者相对路径都可）setting 目录下的 aaa.json 文件打包到目标的 setting 文件夹下\n",
    "\n",
    "```sh\n",
    "--add-data \".\\\\setting\\\\aaa.json;.\\\\setting\"\n",
    "```\n",
    "\n",
    "例：  \n",
    "将原路径（绝对路径或者相对路径都可）config 目录下的所有文件 文件打包到目标的 config 文件夹下\n",
    "\n",
    "```sh\n",
    "--add-data \".\\\\config\\\\*;.\\\\config\"\n",
    "```\n",
    "\n",
    "#### 修改 spec 模式\n",
    "\n",
    "```py\n",
    "datas=[('.\\\\config\\\\*', '.\\\\config'),\n",
    "('.\\\\setting\\\\aaa.json', '\\\\setting') ],\n",
    "```\n",
    "\n",
    "#### 💊 注意事项\n",
    "\n",
    "将文件打包成单文件时（但文件夹除外），你在程序中访问这些静态文件时**切勿使用相对路径**，因为单文件在运行时先解压所有需要的文件到 用户临时文件夹下（ /temp 目录），并不在你当前运行路径下寻找这些静态文件。你需要将程序里访问这些静态文件的路径改为绝对路径，才能在其解压出的临时路径下找到他们：\n",
    "\n",
    "```py\n",
    "if getattr(sys, 'frozen', None):\n",
    "    basedir = sys._MEIPASS\n",
    "else:\n",
    "    basedir = os.path.dirname(__file__)\n",
    "\n",
    "#接上例，打包进去的 aaa.json，加到了这个绝对目录。\n",
    "aaa = os.path.join(basedir, 'setting', 'aaa.json')\n",
    "```\n",
    "\n",
    "👉 效果参考：\n",
    "\n",
    "https://blog.csdn.net/Iv_zzy/article/details/107407167\n",
    "\n",
    "## 3. 打包二进制依赖文件\n",
    "\n",
    "将程序中依赖的 dll 或者 so 文件 一起打包进去。（编译好的 python 可调用模块或者 ctypes 加载的 dll 文件都可）\n",
    "\n",
    "> --add-binary <SRC;DEST or SRC:DEST>\n",
    "\n",
    "可以多次使用；参数格式参考上一小标题（打包静态文件）\n",
    "\n",
    "例：\n",
    "\n",
    "```sh\n",
    "--add-binary D:\\\\test\\\\pack\\\\HCNetSDK.dll;.\\\\lib\n",
    "```\n",
    "\n",
    "### 4. 加密字节码\n",
    "\n",
    "详细介绍：https://pyinstaller.readthedocs.io/en/stable/usage.html#encrypting-python-bytecode\n",
    "\n",
    "使用 --key 参数 指定一个长度为 16 的字符串，来加密 python 字节码文件，\n",
    "你需要先执行以下命令\n",
    "\n",
    "```sh\n",
    "pip install pyinstaller[encryption]\n",
    "```\n",
    "\n",
    "然后\n",
    "\n",
    "```sh\n",
    "pyinstaller.exe --key=xxxx -F hellow.py\n",
    "```\n",
    "\n",
    "（🙏 过程可能不太顺利，过程慢长，打包完文件也较大）\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
